/*
 * compile using:
 * gcc -Wall -Wextra packet.c `pkg-config --cflags opencv` `pkg-config --libs opencv` -o packet.exe
 */
#include "packet.h"
#include "cloud_helper.h"

void close_cloud_con(cloud_server_t* server) {
    close(server->port);
}

void free_server_list(server_list* s_list) {
    int i;
    for (i=0; i<s_list->num_servers; i++) {
        if (s_list->servers[i] == NULL) {
            // end of list
            break;
        }
        close_cloud_con(s_list->servers[i]);
    }
    free(s_list);
}


// get sockaddr, IPv4 or IPv6:
void *get_in_addr2(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

/*
 * makes packet for sending to client
 */
unsigned char* get_rtp_packet(char* filename, int frame_num, int seq_num, server_list* s_list) {
	// size of packet is suffix + header + payload
	//               =   4    +   12   + cv->encoded->cols
    // get frame from cloud
    const cloud_server_t *cserver = get_cloud_server(filename, frame_num);
    /*
    if server_in_list(cserver->server, s_list) {
        //
    } else {
        // all that...
    }
    */

    int sockfd;
    char *received = (char*)malloc(sizeof(char)*MAXDATASIZE);
    char* frame;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(cserver->server, (char*)(&cserver->port), &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return NULL;
    }

    // loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: connect");
            continue;
        }
        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return NULL;
    }

    // set timeout on socket
    struct timeval timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
        fprintf(stderr,"setsockopt failed\n");
    }

    inet_ntop(p->ai_family, get_in_addr2((struct sockaddr *)p->ai_addr),
            s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    int numbytes;

    if ((numbytes = recv(sockfd, received, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }
    received[numbytes] = '\0';
    printf("client: received '%s' from cloud\n",received);

    // get frame size information
    if (numbytes < 5) {
        perror("less than 5 bytes received");
        close(sockfd);
        return NULL;
    }
    int frame_size = (1000*received[0] + 100*received[1] + 10*received[2] + received[3]);
    if (frame_size == 0) {
        perror("frame size is 0");
        close(sockfd);
        return NULL;
    }

    frame = (char*)malloc(sizeof(char)*frame_size);
    received = received + 5; // ignore first 5 chars
    memcpy (frame, received, frame_size);

    int bytes_left = frame_size + 5 - MAXDATASIZE;
    while (bytes_left > 0) {
        // the whole frame didn't fit into your buffer
        // get more bytes
        if ((numbytes = recv(sockfd, received, MAXDATASIZE-1, 0)) == -1) {
            perror("recv");
            break;
        }
        received[numbytes] = '\0';
        printf("client: received '%s' from cloud\n",received);
        strncat(frame, received, numbytes);
        bytes_left = bytes_left - numbytes;
    }

    free(received);

	unsigned char *packet = (unsigned char*) malloc(sizeof(unsigned char) * (frame_size + 16));
	memset(packet, 0, frame_size + 16);
	// make prefix: 0x2400XXXX, where 0xXXXX is size of packet
	// size of packet = size of paylod + size of header (12 bytes)
	// 0x24 = $, bit-shifted into most significant position
	unsigned long packet_size = frame_size + 12;
	unsigned long prefix = packet_size + (0x24 << 24);
	printf("size of frame: %d\n", 212);
	printf("prefix: 0x%lx\n", prefix);
	unsigned char buf[4];
	buf[0] = (unsigned char) 0x24;
	buf[1] = (unsigned char) 0x00;
	buf[2] = (unsigned char) ((packet_size >> 8) & 0x000000ff);
	buf[3] = (unsigned char) (packet_size & 0x000000ff);
	// make header
	unsigned char attrs[4];
	attrs[0] = (unsigned char) 0x80;
	attrs[1] = (unsigned char) JPEG; // payload type
	attrs[2] = (unsigned char) seq_num >> 8;
	attrs[3] = (unsigned char) seq_num;
	// csrc header
	unsigned char csrc[4] = { 0x00, 0x00, 0x00, 0x00 };
	// timestamp header
	int timestamp = frame_num * INTERVAL_MS;
	unsigned char time[4];
	time[0] = (unsigned char) (timestamp >> 24);
	time[1] = (unsigned char) (timestamp >> 16);
	time[2] = (unsigned char) (timestamp >> 8);
	time[3] = (unsigned char) timestamp;
	// put it together

    set_buffer(packet, buf, 4);
	set_buffer(packet + 4, attrs, 4);
	set_buffer(packet + 8, time, 4);
	set_buffer_to(packet + 12, 0, 4); //dummy SSRC
	set_buffer(packet + 16, frame, frame_size);
    
	return packet;
}


void set_buffer(unsigned char* to, unsigned char* from, unsigned long bytes) {
	unsigned long x = 0;
	unsigned char* current_to = to;

	for (x = 0; x < bytes; x++) {
		*(current_to++) = from[x];
	}
}

void set_buffer_to(unsigned char* to, unsigned char from, unsigned long bytes) {
	unsigned long x = 0;
	unsigned char* current_to = to;

	for (x = 0; x < bytes; x++) {
		*(current_to++) = from;
	}
}

/*
 * initializes video_manager struct, opens file
 * returns 1 on error, 0 on success
 */
// function moved to inside of init_timer() in fsm
int init_video_manager(video_manager* cv, char* filename) {
	cv->image = (IplImage *) malloc(sizeof(IplImage));

	CvMat *thumb = (CvMat *) malloc(sizeof(CvMat));
	thumb = cvCreateMat(WIDTH, HEIGHT, CV_8UC3);
	cv->thumb = thumb;

	cv->encoded = (CvMat *) malloc(sizeof(CvMat));

	cv->video = cvCaptureFromFile(filename);
	if (!cv->video) {
		// The file doesn't exist or can't be captured as a video file.
		perror("could not open file");
		return 1;
	}
	return 0;
}

/*
 * gets frame from video
 * returns 1 on error, 0 on success
 * TODO: remove frame_num dependency, since that function does not work
 */
int get_frame(video_manager* cv, int frame_num) {
	// Position the video at a specific frame number position
	// NOTE: PROBABLY DOES NOT DO ANYTHING
	cvSetCaptureProperty(cv->video, CV_CAP_PROP_POS_FRAMES, frame_num);
	cv->image = cvQueryFrame(cv->video);
	if (!cv->image) {
		// Next frame doesn't exist or can't be obtained.
		perror("bad frame");
		return 1;
	}
	// use thumb to resize
	cvResize(cv->image, cv->thumb, CV_INTER_AREA);
	// Encode the frame in JPEG format with JPEG quality 30%.
	static const int encodeParams[] = { CV_IMWRITE_JPEG_QUALITY, 30 };
	cv->encoded = cvEncodeImage(".jpeg", cv->thumb, encodeParams);
	// After the call above, the encoded data is in encoded->data.ptr
	// and has a length of encoded->cols bytes.
	return 0;
}

/*
 * closes video
 */
void close_video(video_manager* cv) {
	cvReleaseCapture(&cv->video);
}

/*
 * for testing
 */
/*
int main() {

	char* filename = "sample.avi";
	video_manager *cv = (video_manager*) malloc(sizeof(video_manager));

	// set up cv
	cv->image = (IplImage *) malloc(sizeof(IplImage));
	cv->thumb = cvCreateMat(WIDTH, HEIGHT, CV_8UC3);
	cv->encoded = (CvMat *) malloc(sizeof(CvMat));
	cv->video = cvCaptureFromFile(filename);
	if (!cv->video) {
		// The file doesn't exist or can't be captured as a video file.
		perror("bad file");
		return 1;
	}

	unsigned char* packet = get_rtp_packet(cv, 3, 564);
	if (!packet) {
		perror("could not get packet");
		return 1;
	}

	// start parsing packet, yo
	int i;

	printf("parsed prefix: 0x");
	for (i = 0; i < 4; i++) {
		printf("%x,", (unsigned char) packet[i] & 0xff);
	}
	printf("\n");

	printf("parsed attrs: 0x");
	for (i = 0; i < 4; i++) {
		printf("%x,", (unsigned char) packet[4 + i] & 0xff);
	}
	printf("\n");

	printf("parsed csrc: 0x");
	for (i = 0; i < 4; i++) {
		printf("%x,", (unsigned char) packet[8 + i] & 0xff);
	}
	printf("\n");

    printf("parsed timestamp: 0x");
	for (i = 0; i < 4; i++) {
		printf("%x,", (unsigned char) packet[12 + i] & 0xff);
	}
	printf("\n");

	close_video(cv);

	return 0;
}
*/
